package com.ruoyi.business.service.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.word.WordExportUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.*;
import cn.hutool.extra.qrcode.QrCodeUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.business.domain.*;
import com.ruoyi.business.enums.*;
import com.ruoyi.business.service.*;
import com.ruoyi.business.utils.*;
import com.ruoyi.business.vo.*;
import com.ruoyi.business.vo.export.ExportBadgeVo;
import com.ruoyi.business.vo.export.RegisterImportVo;
import com.ruoyi.business.vo.export.RegisterLevelVo;
import com.ruoyi.business.vo.export.RegisterScoreVo;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.RoleConstants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.service.ISysUserService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.openxml4j.util.ZipSecureFile;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import static com.ruoyi.common.core.domain.AjaxResult.CODE_TAG;


/**
 * @author Tellsea
 * @date 2024/1/26
 */
@Slf4j
@Service
@AllArgsConstructor
@Transactional
public class TRegisterBaseServiceImpl implements ITRegisterBaseService {

    private final ITRegisterDeptService registerDeptService;
    private final ITRegisterInfoService registerInfoService;
    private final ITAuditLogService auditLogService;
    private final ITRegisterNoticeService registerNoticeService;
    private final ITMessageInfoService messageInfoService;
    private final ITSureInfoService sureInfoService;
    private final ITRegisterAllocationService registerAllocationService;
    private final ITRegisterLeaveService registerLeaveService;
    private final SmsService smsService;
    private final ISysUserService sysUserService;
    private final ITCooperateInfoService cooperateInfoService;

    private final ITUserOptLogService tUserOptLogService;

    private final ISysAreaService sysAreaService;

    private final ITRegisterWorkService registerWorkService;

    private final Environment env;

    private final Lock lock = new ReentrantLock();
    private final Lock lock1 = new ReentrantLock();

    @Override
    public TRegisterBaseVo queryByCount(TRegisterBaseVo entity) {
        return registerInfoService.queryByCount(entity);
    }

    @Override
    public TableDataInfo<TRegisterBaseVo> queryByDept(TRegisterBaseVo entity) {
        return registerInfoService.queryByDept(entity);
    }

    /**
     * AuditLevel  问题：0 待审批，1 是科室审批过了，2 是管理员审批过了
     * @param entity
     * @return
     */
    @Override
    public AjaxResult saveAudit(TAuditLogVo entity) {
        SysUser user = SecurityUtils.getLoginUser().getUser();
        boolean isDeptManage = SecurityUtils.hasRole(RoleConstants.DEPT_MANAGE.getRoleKey());
        boolean isSecretary = SecurityUtils.hasRole(RoleConstants.SECRETARY.getRoleKey());
        boolean isSystem = SecurityUtils.hasRole(RoleConstants.SYSTEM.getRoleKey());
        boolean isAdmin = SecurityUtils.hasRole(RoleConstants.ADMIN.getRoleKey());
        if (!isSystem && !isDeptManage && !isSecretary && !isAdmin) {
            throw new ServiceException("只有系统管理员、进修科主任、进修科秘书角色可以审核!");
        }
        for (Long registerId : entity.getRegisterIdList()) {
            TRegisterInfo registerInfo = registerInfoService.getById(registerId);
            if(registerInfo.getAuditLevel()==null){
                registerInfo.setAuditLevel(0L);
            }
            // 根据状态判断等级 0:暂存,1:待审批,2:转审中,3:审批通过,4:审批拒绝,5:已退回
            // 问题：0 待审批，1 是科室审批过了，2 是管理员审批过了
            if(Objects.equals(registerInfo.getAuditStatus(), AuditStatusEnums.WAIT.getCode())){
                registerInfo.setAuditLevel(1L);
            }
            if(registerInfo.getAuditLevel()>2L){
                // 将错误数据设置为转审中
                registerInfo.setAuditLevel(1L);
            }
            TAuditLogVo auditLogVo = new TAuditLogVo();
            auditLogVo.setRegisterId(registerId);
            List<TAuditLogVo> tAuditLogVos = auditLogService.queryAll(auditLogVo);
            if(tAuditLogVos!=null && !tAuditLogVos.isEmpty()){
                // 如果审批记录最新审批是退回，那么当前审批别减1
                TAuditLogVo tAuditLogVo = tAuditLogVos.get(0);
                if(tAuditLogVo.getAuditStatus()==2){
                    if(tAuditLogVo.getAuditLevel()>2L){
                        tAuditLogVo.setAuditLevel(2L);
                    }
                    registerInfo.setAuditLevel(tAuditLogVo.getAuditLevel()-1<0?0:tAuditLogVo.getAuditLevel()-1);
                }
            }
//            需求中增加跨部门审批操作，此处需要去掉
//            TRegisterDept registerDept = registerDeptService.getById(registerInfo.getRegisterDeptId());
//            if ((isDeptManage || isSecretary)
//                    && CompareUtil.compare(registerDept.getManageUserId(), user.getUserId()) != 0
//                    && CompareUtil.compare(registerDept.getSecretaryUserId(), user.getUserId()) != 0) {
//                throw new ServiceException("审核失败，进修科室与您不匹配");
//            }
            TAuditLog auditLog = new TAuditLog()
                    .setType(1L)
                    .setRegisterId(registerId)
                    .setAuditUserId(user.getUserId())
                    .setAuditStatus(entity.getAuditStatus())
                    .setRemark(entity.getRemark());
            if (CompareUtil.compare(registerInfo.getAuditLevel(), 1L) == 0 && (!isSystem && !isAdmin)) {
                throw new ServiceException("学员[" + registerInfo.getUserName() + "]需要使用管理员进行二级审批");
            }
            String optDesc = "";
            if (CompareUtil.compare(entity.getAuditStatus(), 1L) == 0) {
                registerInfo.setAuditLevel(registerInfo.getAuditLevel() + 1);
                // 科室负责人审核
                if (CompareUtil.compare(registerInfo.getAuditLevel(), 1L) == 0) {
                    registerInfo.setAuditStatus(AuditStatusEnums.WAIT.getCode());
                }
                // 管理员审核
                if (CompareUtil.compare(registerInfo.getAuditLevel(), 2L) == 0) {
                    registerInfo.setAuditStatus(AuditStatusEnums.SUCCESS.getCode());
                    registerInfo.setNoticeStatus(NoticeStatusEnums.NO.getCode());
                }
                optDesc = "审批意见【同意】";
            } else if (CompareUtil.compare(entity.getAuditStatus(), 2L) == 0) {
                registerInfo.setAuditLevel(registerInfo.getAuditLevel() + 1);
                optDesc = "审批意见【退回】";
                registerInfo.setAuditStatus(AuditStatusEnums.RETURNED.getCode());
                smsService.sendMessage(registerInfo.getPhoneNumber(), "学员" + registerInfo.getUserName() + "您好，您在复旦大学华山医院进修管理平台报名的申请，【材料已退回】，请登录系统修改后重新提交。我的申请地址：https://jx.edu-huashan.com:18084/engage-web/apply");
            } else if (CompareUtil.compare(entity.getAuditStatus(), 3L) == 0) {
                registerInfo.setAuditLevel(registerInfo.getAuditLevel() + 1);
                optDesc = "审批意见【拒绝】";
                registerInfo.setAuditStatus(AuditStatusEnums.FAIL.getCode());
                smsService.sendMessage(registerInfo.getPhoneNumber(), "学员" + registerInfo.getUserName() + "您好，您在复旦大学华山医院进修管理平台报名的申请，【材料审核失败】。");
            }
            auditLog.setAuditLevel(registerInfo.getAuditLevel());
            auditLogService.save(auditLog);
            registerInfoService.updateById(registerInfo);

            List<String> roleList = new ArrayList<>();
            if(user.getRoles()!=null){
                for (SysRole role : user.getRoles()) {
                    roleList.add(role.getRoleName());
                }
            }
            TUserOptLog tUserOptLog = new TUserOptLog()
                    .setOptDesc("报名审批："+optDesc)
                    .setOptTime(auditLog.getUpdateTime())
                    .setFromUserid(user.getUserId().toString())
                    .setFromUsername(user.getNickName())
                    .setFromRolename(StringUtils.join(roleList,","))
                    .setToUserid(registerId.toString())
                    .setToUsername(registerInfo.getUserName());
            tUserOptLogService.save(tUserOptLog);
        }
        return AjaxResult.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveNotice(TRegisterNoticeVo entity) throws Exception {
        // 校验报名是否已发送了通知
        judgeNoticeSend(entity);

        // 录取通知
        TSureInfo sureInfo = sureInfoService.getById(1);
        Long userId = SecurityUtils.getLoginUser().getUser().getUserId();
        List<String> errorUserList = new ArrayList<>();
        for (Long registerId : entity.getRegisterIdList()) {
            String userName = null;
            try {
                List<TRegisterNotice> tRegisterNotices = registerNoticeService.list(Wrappers.<TRegisterNotice>lambdaQuery()
                        .eq(TRegisterNotice::getRegisterId, registerId));
                entity.setRegisterId(registerId);
                TRegisterInfo registerInfo = registerInfoService.getById(registerId);
                if(Objects.equals(registerInfo.getNoticeStatus(), NoticeStatusEnums.SUCCESS.getCode())){
                    throw new Exception("学员【"+registerInfo.getUserName()+"】已确认将按时来院报到，故无法再次发送通知书");
                }
                userName = registerInfo.getUserName();
                TRegisterDept registerDept = registerDeptService.getById(registerInfo.getRegisterDeptId());
                String jobNumber;
                if(!CollectionUtils.isEmpty(tRegisterNotices)){
                    jobNumber = tRegisterNotices.get(0).getJobNumber();
                }else {
                    jobNumber = createJobNumber(registerDept);
                }
                entity.setCreateTime(new Date());
                entity.setUpdateTime(new Date());
                entity.setRegisterDeptId(registerDept.getId());
                entity.setJobNumber(jobNumber);
                entity.setEndDateDefault(entity.getEndDate());
                entity.setNoticeContent(sureInfo.getNoticeContent());
                registerNoticeService.save(entity);
                // 系统消息
                messageInfoService.save(new TMessageInfo()
                        .setType(MessageTypeEnums.NOTICE.getCode())
                        .setRefId(registerInfo.getId())
                        .setSendUserId(userId)
                        .setReceiveUserId(registerInfo.getUserId())
                        .setContent("学员您好，您的录取状态已经更新了，请登录[华山医院教育一网通办]系统查看消息！")
                        .setReadStatus(MessageReadStatusEnums.UNREAD.getCode()));
                registerInfo.setNoticeStatus(NoticeStatusEnums.WAIT.getCode());
                registerInfoService.updateById(registerInfo);
                // 短信
                smsService.sendMessage(registerInfo.getPhoneNumber(), "学员" + registerInfo.getUserName() + "您好，您的报名已通过，请登录系统查看录取通知书！");
            }catch (Exception e){
                if(StringUtils.isNotEmpty(userName)){
                    errorUserList.add(userName);
                    log.error("学员:{},录取通知发送失败！！！,{}", userName, e.getMessage(), e);
                }else{
                    log.error("录取通知发送失败！！！,{}",  e.getMessage(), e);
                }
            }
        }
        if(!errorUserList.isEmpty()){
            String errMsg = StringUtils.join(errorUserList,",");
           throw new Exception(errMsg + "学员发送通知失败");
        }
    }

    /**
     * 判断是否已发送了通知
     * @param entity
     */
    @Override
    public void judgeNoticeSend(TRegisterNoticeVo entity) {
        if(entity.getRegisterIdList()!=null && !entity.getRegisterIdList().isEmpty()){
            int tRegisterNoticesCnt = registerInfoService.count(Wrappers.<TRegisterInfo>lambdaQuery()
                    .in(TRegisterInfo::getId, entity.getRegisterIdList())
                    .eq(TRegisterInfo::getNoticeStatus,NoticeStatusEnums.SUCCESS.getCode()));
            if(tRegisterNoticesCnt > 0){
                throw new BaseException("请选择没有确认报到的学员发送通知");
            }
        }
    }

    /**
     * 1、JX：不变化
     * 2、年份（25）：明年就是26
     * 3、进修科目编号：获取进修科室/进修科目编号字段，个位数的，显示01、02...
     * 3、序号000：每年、每个进修科目 重新从001开始计数
     * @param registerDept 进修科室
     * @return 生成的编号
     */
    @Override
    public String createJobNumber(TRegisterDept registerDept) {
        lock.lock();
        try {
            String jobNumber;
            jobNumber = "JX";
            // JX年份+科室编码+001+X
            jobNumber += Integer.parseInt(DateUtil.year(DateUtil.date()) % 100 + "");
            jobNumber += registerDept.getRegisterDeptNumber();
//            int registerCount = registerNoticeService.count(Wrappers.<TRegisterNotice>lambdaQuery()
//                    .eq(TRegisterNotice::getRegisterDeptId, registerDept.getId()));
//            registerCount = registerCount + 1;
//            jobNumber += String.format("%03d", registerCount);
//            jobNumber += "X";
            return registerNoticeService.getNewJobNumber(jobNumber);
        }catch (Exception e){
            throw new ServiceException(e.getMessage());
        }finally {
            lock.unlock();
        }
    }

    @Override
    public AjaxResult saveSource(TRegisterInfoVo entity) {
        for (Long registerId : entity.getRegisterIdList()) {
            entity.setId(registerId);
            registerInfoService.updateById(entity);
        }
        return AjaxResult.success();
    }

    @Override
    public AjaxResult updateNotice(TRegisterInfoVo entity) {
        for (Long registerId : entity.getRegisterIdList()) {
            entity.setId(registerId);
            registerInfoService.updateById(entity);
        }
        return AjaxResult.success();
    }

    @Override
    public void exportBadge(Long[] ids) {
        TRegisterInfoVo entity = new TRegisterInfoVo();
        entity.setNoAuth(true);
        entity.setRegisterIdList(Arrays.asList(ids));
        List<TRegisterInfoVo> list = registerInfoService.queryAllSingle(entity);
        if (CollUtil.isEmpty(list)) {
            throw new ServiceException("没有需要导出的数据");
        }
        try {
            String projectPath = System.getProperty("user.dir") + "/uploadPath";
            List<File> sourceFiles = new ArrayList<>();
            // 图片
            for (TRegisterInfoVo vo : list) {
                if(vo.getElectronicIdPhoto()==null){
                    log.error("学员【{}】工牌照片不存在", vo.getUserName());
                    continue;
                }
                String srcPath = projectPath + vo.getElectronicIdPhoto().replace("/profile", "");
                String jobNumber = ObjectUtil.isNotEmpty(vo.getRegisterNoticeVo()) ? vo.getRegisterNoticeVo().getJobNumber() : "无工号";
                String targetPath = srcPath.substring(0, srcPath.lastIndexOf("/") + 1) + jobNumber + "-" + vo.getUserName() + "." + FileUtil.getSuffix(srcPath);
                if(FileUtil.isFile(srcPath)){
                    File targetFile = FileUtil.copy(srcPath, targetPath, true);
                    sourceFiles.add(targetFile);
                }else{
                    log.error("学员【{}】工牌不存在", vo.getUserName());
                }

            }
            // 基础信息汇总表
            List<ExportBadgeVo> exportList = new ArrayList<>();
            list.forEach(e -> {
                String jobNumber = "";
                String endDate = "";
                if (ObjectUtil.isNotEmpty(e.getRegisterNoticeVo())) {
                    jobNumber = e.getRegisterNoticeVo().getJobNumber();
                    endDate = e.getRegisterNoticeVo().getEndDate();
                }
                exportList.add(ExportBadgeVo.builder()
                        .userName(e.getUserName())
                        .englishName(NameUtils.toEnglishPinyin(e.getUserName()))
                        .jobNumber(jobNumber)
                        .registerDeptName(e.getRegisterDeptName())
                        .pictureName(jobNumber + "-" + e.getUserName())
                        .departureDate(endDate)
                        .build());
            });
            String date = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss");
            if (CollUtil.isNotEmpty(exportList)) {
                String excelPath = projectPath + "/upload/" + DateUtil.format(DateUtil.date(), "yyyy/MM/dd/");
                String filName = "基础信息汇总表-" + date + ".xlsx";
                File excelPathFile = FileUtil.file(excelPath);
                if (!excelPathFile.exists()) {
                    excelPathFile.mkdirs();
                }
                ExportParams exportParams = new ExportParams("基础信息汇总表", "sheet1");
                exportParams.setStyle(EasypoiExcelStyle.class);
                Workbook workbook = ExcelExportUtil.exportExcel(exportParams, ExportBadgeVo.class, exportList);
                FileOutputStream fos = new FileOutputStream(excelPath + filName);
                workbook.write(fos);
                sourceFiles.add(FileUtil.file(excelPath + filName));
            }
            // 压缩
            String dateFormat = DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN);
            String dateFolder = DateUtil.format(DateUtil.date(), "yyyy/MM/dd/");
            File targetZipFile = FileUtil.file(projectPath + "/upload/" + dateFolder + "导出工牌-" + dateFormat + ".zip");
            ZipFileUtils.generateZip(targetZipFile, sourceFiles, false);
            for (File sourceFile : sourceFiles) {
                FileUtil.del(sourceFile);
            }
            ZipFileUtils.downloadZip(targetZipFile, ServletUtils.getResponse());
        } catch (Exception e) {
            log.error("文件下载异常", e);
            throw new ServiceException("文件下载异常，请联系管理员");
        }
    }

    @Override
    public void exportBadgeSingle(List<TRegisterInfoVo> list, HttpServletResponse response) {
        if (CollUtil.isEmpty(list)) {
            throw new ServiceException("没有需要导出的数据");
        }
        try {
            String projectPath = System.getProperty("user.dir") + "/uploadPath";
            List<File> sourceFiles = new ArrayList<>();
            // 图片
            for (TRegisterInfoVo vo : list) {
                String electronicIdPhoto = vo.getElectronicIdPhoto();
                if(electronicIdPhoto ==null){
                    log.error("学员【{}】工牌照片不存在", vo.getUserName());
                    continue;
                }
                if(electronicIdPhoto.contains(",")){
                    // 多张电子照片
                    electronicIdPhoto = electronicIdPhoto.split(",")[0];
                }
                String srcPath = projectPath + electronicIdPhoto.replace("/profile", "");
                String jobNumber = ObjectUtil.isNotEmpty(vo.getRegisterNoticeVo()) ? vo.getRegisterNoticeVo().getJobNumber() : "无工号";
                String targetPath = srcPath.substring(0, srcPath.lastIndexOf("/") + 1) + jobNumber + "-" + vo.getUserName() + "." + FileUtil.getSuffix(srcPath);
                if(FileUtil.isFile(srcPath)){
                    File targetFile = FileUtil.copy(srcPath, targetPath, true);
                    sourceFiles.add(targetFile);
                }else{
                    log.error("学员【{}】工牌不存在", vo.getUserName());
                }

            }
            // 基础信息汇总表
            List<ExportBadgeVo> exportList = new ArrayList<>();
            list.forEach(e -> {
                String jobNumber = "";
                String endDate = "";
                if (ObjectUtil.isNotEmpty(e.getRegisterNoticeVo())) {
                    jobNumber = e.getRegisterNoticeVo().getJobNumber();
                    endDate = e.getRegisterNoticeVo().getEndDate();
                }
                exportList.add(ExportBadgeVo.builder()
                        .userName(e.getUserName())
                        .englishName(NameUtils.toEnglishPinyin(e.getUserName()))
                        .jobNumber(jobNumber)
                        .registerDeptName(e.getRegisterDeptName())
                        .pictureName(jobNumber + "-" + e.getUserName())
                        .departureDate(endDate)
                        .build());
            });
            String date = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss");
            if (CollUtil.isNotEmpty(exportList)) {
                String excelPath = projectPath + "/upload/" + DateUtil.format(DateUtil.date(), "yyyy/MM/dd/");
                String filName = "基础信息汇总表-" + date + ".xlsx";
                File excelPathFile = FileUtil.file(excelPath);
                if (!excelPathFile.exists()) {
                    excelPathFile.mkdirs();
                }
                ExportParams exportParams = new ExportParams("基础信息汇总表", "sheet1");
                exportParams.setStyle(EasypoiExcelStyle.class);
                Workbook workbook = ExcelExportUtil.exportExcel(exportParams, ExportBadgeVo.class, exportList);
                FileOutputStream fos = new FileOutputStream(excelPath + filName);
                workbook.write(fos);
                sourceFiles.add(FileUtil.file(excelPath + filName));
            }
            // 压缩
            String dateFormat = DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN);
            String dateFolder = DateUtil.format(DateUtil.date(), "yyyy/MM/dd/");
            File targetZipFile = FileUtil.file(projectPath + "/upload/" + dateFolder + "导出工牌-" + dateFormat + ".zip");
            ZipFileUtils.generateZip(targetZipFile, sourceFiles, false);
            for (File sourceFile : sourceFiles) {
                FileUtil.del(sourceFile);
            }
            ZipFileUtils.downloadZip(targetZipFile, response);
        } catch (Exception e) {
            log.error("文件下载异常", e);
            throw new ServiceException("文件下载异常，请联系管理员");
        }
    }

    @Override
    public void genQrCode() {
        String urlAddress = env.getProperty("web.url-address");
        String url = urlAddress+"/engage-user/#/";
        BufferedImage bufferedImage = QrCodeUtil.generate(url, 300, 300);
        try {
            ServletOutputStream os = ServletUtils.getResponse().getOutputStream();
            ImageIO.write(bufferedImage, "PNG", os);
            os.flush();
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public AjaxResult saveAllocation(TRegisterAllocationVo entity) {
        for (Long registerId : entity.getRegisterIdList()) {
            for (Long teacherUserId : entity.getTeacherUserIdList()) {
                LambdaQueryWrapper<TRegisterAllocation> wrapper = Wrappers.<TRegisterAllocation>lambdaQuery()
                        .eq(TRegisterAllocation::getRegisterId, registerId)
                        .eq(TRegisterAllocation::getTeacherUserId, teacherUserId);
                int count = registerAllocationService.count(wrapper);
                if (CompareUtil.compare(count, 0) == 0) {
                    registerAllocationService.save(new TRegisterAllocation()
                            .setRegisterId(registerId)
                            .setTeacherUserId(teacherUserId)
                            .setScoreProfessional(BigDecimal.ZERO)
                            .setScoreLabour(BigDecimal.ZERO)
                            .setScoreDuty(BigDecimal.ZERO)
                            .setScoreOperate(BigDecimal.ZERO)
                            .setScoreMedicalHistory(BigDecimal.ZERO)
                            .setScoreTotal(BigDecimal.ZERO)
                            .setScoreStatus(ScoreStatusEnums.NO.getCode()));
                }
            }
            registerAllocationService.remove(Wrappers.<TRegisterAllocation>lambdaQuery()
                    .eq(TRegisterAllocation::getRegisterId, registerId)
                    .notIn(TRegisterAllocation::getTeacherUserId, entity.getTeacherUserIdList()));
            List<TRegisterAllocation> allocationList = registerAllocationService.list(Wrappers.<TRegisterAllocation>lambdaQuery()
                    .eq(TRegisterAllocation::getRegisterId, registerId));
            registerInfoService.update(null, Wrappers.<TRegisterInfo>lambdaUpdate()
                    .set(TRegisterInfo::getAllocationStatus, AllocationStatusEnums.SUCCESS.getCode())
                    .set(TRegisterInfo::getCultureStatus, CultureStatusEnums.ING.getCode())
                    .set(TRegisterInfo::getLeaveStatus, LeaveStatusEnums.FULL.getCode())
                    .set(TRegisterInfo::getCurrentStatus, CurrentStatusEnums.NO.getCode())
                    .set(TRegisterInfo::getTeacherUserId, allocationList.stream()
                            .map(TRegisterAllocation::getTeacherUserId)
                            .map(Object::toString)
                            .collect(Collectors.joining(",")))
                    .eq(TRegisterInfo::getId, registerId));

            SysUser loginUser = SecurityUtils.getLoginUser().getUser();
            List<String> roleList = new ArrayList<>();
            if(loginUser.getRoles()!=null){
                for (SysRole role : loginUser.getRoles()) {
                    roleList.add(role.getRoleName());
                }
            }
            TUserOptLog tUserOptLog = new TUserOptLog()
                    .setOptDesc("分配带教")
                    .setOptTime(DateUtils.getNowDate())
                    .setFromUserid(loginUser.getUserId().toString())
                    .setFromUsername(loginUser.getNickName())
                    .setFromRolename(StringUtils.join(roleList,","))
                    .setToUserid(registerId.toString());
            tUserOptLogService.save(tUserOptLog);
        }
        return AjaxResult.success();
    }

    @Override
    public AjaxResult saveModify(TRegisterNoticeVo entity) {
        for (Long registerId : entity.getRegisterIdList()) {
            TRegisterNoticeVo registerNoticeVo = registerNoticeService.queryByRegisterId(registerId);
            registerNoticeVo.setEndDate(entity.getEndDate());
            registerNoticeVo.setEngageRemark(entity.getEngageRemark());
            // 修改进修期限
            long day = DateUtil.betweenDay(DateUtil.parse(registerNoticeVo.getStartDate()), DateUtil.parse(registerNoticeVo.getEndDate()), false);
            // 更新进修状态
            registerInfoService.updateById(new TRegisterInfo().setId(registerId)
                    .setDurationName(day + "天"));
            if(entity.getGraduationStatus()!=null){
                TRegisterInfo tRegisterInfo = new TRegisterInfo().setId(registerId)
                        .setGraduationStatus(entity.getGraduationStatus());
                if(Objects.equals(entity.getGraduationStatus(), GraduationStatusEnums.STOP.getCode())){
                    tRegisterInfo.setCultureStatus(CultureStatusEnums.STOP.getCode());
                }
                registerInfoService.updateById(tRegisterInfo);
            }
            // 更新结业日期、进修备注
            registerNoticeService.updateById(registerNoticeVo);
        }
        return AjaxResult.success();
    }

    @Override
    public void scoreExport(TRegisterInfoVo entity) {
        boolean isTeacher = SecurityUtils.hasRole(RoleConstants.TEACHER.getRoleKey());
        if (!isTeacher) {
            throw new ServiceException("只有带教老师角色可以导入、导出考评分数！");
        }
        Long userId = SecurityUtils.getLoginUser().getUser().getUserId();
        List<TRegisterInfoVo> list = registerInfoService.queryAll(entity);
        ExcelUtil<RegisterScoreVo> util = new ExcelUtil<>(RegisterScoreVo.class);
        List<RegisterScoreVo> targetList = new ArrayList<>();
        list.forEach(e -> {
            List<TRegisterAllocationVo> allocationVoList = e.getRegisterAllocationVoList();
            if (CollUtil.isEmpty(allocationVoList)) {
                throw new ServiceException("【" + e.getUserName() + "】没有分配带教老师");
            }
            RegisterScoreVo vo = new RegisterScoreVo();
            vo.setRegisterId(e.getId());
            vo.setRegisterDeptName(e.getRegisterDeptName());
            vo.setUserName(e.getUserName());
            if (ObjectUtil.isEmpty(e.getRegisterNoticeVo())) {
                throw new ServiceException("数据异常，没有录取通知");
            }
            vo.setJobNumber(e.getRegisterNoticeVo().getJobNumber());
            vo.setDurationName(e.getDurationStartDate() + " - " + e.getDurationEndDate() + "（" + e.getDurationName() + "）");
            vo.setUnitHospitalName(e.getUnitHospitalName());
            vo.setCultureStatusName(e.getCultureStatusName());
            // 当前登录账号的老师
            TRegisterAllocationVo registerAllocationVo = allocationVoList.stream()
                    .filter(allocationVo -> CompareUtil.compare(allocationVo.getTeacherUserId(), userId) == 0)
                    .findFirst().orElse(null);
            if (ObjectUtil.isEmpty(registerAllocationVo)) {
                throw new ServiceException("数据异常，没有设置带教老师");
            }
            vo.setRegisterAllocationId(registerAllocationVo.getId());
            vo.setTeacherNickName(registerAllocationVo.getTeacherNickName());
            vo.setScoreProfessional(registerAllocationVo.getScoreProfessional());
            vo.setScoreLabour(registerAllocationVo.getScoreLabour());
            vo.setScoreDuty(registerAllocationVo.getScoreDuty());
            vo.setScoreOperate(registerAllocationVo.getScoreOperate());
            vo.setScoreMedicalHistory(registerAllocationVo.getScoreMedicalHistory());
            targetList.add(vo);
        });
        util.exportExcel(ServletUtils.getResponse(), targetList, "Sheet1");
    }

    @Override
    public AjaxResult scoreImport(MultipartFile file) {
        ExcelUtil<RegisterScoreVo> util = new ExcelUtil<>(RegisterScoreVo.class);
        List<RegisterScoreVo> importList;
        try {
            importList = util.importExcel(file.getInputStream());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (CollUtil.isEmpty(importList)) {
            throw new ServiceException("导入数据为空");
        }
        long count = importList.stream().filter(e ->
                BusinessUtils.checkScore(e.getScoreProfessional(), BigDecimal.valueOf(15))
                        || BusinessUtils.checkScore(e.getScoreLabour(), BigDecimal.valueOf(20))
                        || BusinessUtils.checkScore(e.getScoreDuty(), BigDecimal.valueOf(15))
                        || BusinessUtils.checkScore(e.getScoreOperate(), BigDecimal.valueOf(30))
                        || BusinessUtils.checkScore(e.getScoreMedicalHistory(), BigDecimal.valueOf(20))).count();
        if (CompareUtil.compare(count, 0L) > 0) {
            throw new ServiceException("请检查打分项是否在规定范围内：职业道德 (15分)、劳动纪律 (20分)、工作责任性 (15分)、操作能力 (30分)、病史报告书写 (20分)");
        }
        List<TRegisterAllocation> targetList = new ArrayList<>();
        for (RegisterScoreVo vo : importList) {
            TRegisterAllocation allocation = new TRegisterAllocation();
            allocation.setId(vo.getRegisterAllocationId());
            allocation.setScoreProfessional(vo.getScoreProfessional());
            allocation.setScoreLabour(vo.getScoreLabour());
            allocation.setScoreDuty(vo.getScoreDuty());
            allocation.setScoreOperate(vo.getScoreOperate());
            allocation.setScoreMedicalHistory(vo.getScoreMedicalHistory());
            allocation.setScoreTotal(NumberUtil.add(
                    vo.getScoreProfessional(),
                    vo.getScoreLabour(),
                    vo.getScoreDuty(),
                    vo.getScoreOperate(),
                    vo.getScoreMedicalHistory()));
            allocation.setScoreStatus(ScoreStatusEnums.SUCCESS.getCode());
            targetList.add(allocation);
        }
        registerAllocationService.updateBatchById(targetList);
        // 计算平均分
        for (RegisterScoreVo vo : importList) {
            updateScoreTotal(vo.getRegisterId());
        }
        return AjaxResult.success("导入完成");
    }

    @Override
    public void levelExport(TRegisterInfoVo entity) {
        boolean isSecretary = SecurityUtils.hasRole(RoleConstants.SECRETARY.getRoleKey());
        if (!isSecretary) {
            throw new ServiceException("只有教学秘书角色可以导入、导出考评等级！");
        }
        List<TRegisterInfoVo> list = registerInfoService.queryAll(entity);
        ExcelUtil<RegisterLevelVo> util = new ExcelUtil<>(RegisterLevelVo.class);
        List<RegisterLevelVo> targetList = new ArrayList<>();
        list.forEach(e -> {
            List<TRegisterAllocationVo> allocationVoList = e.getRegisterAllocationVoList();
            if (CollUtil.isEmpty(allocationVoList)) {
                throw new ServiceException("【" + e.getUserName() + "】没有分配带教老师");
            }
            long count = allocationVoList.stream().filter(allocationVo ->
                    CompareUtil.compare(allocationVo.getScoreStatus(), ScoreStatusEnums.NO.getCode()) == 0).count();
            if (count > 0) {
                throw new ServiceException("所有带教老师评分完成，带教秘书才能打分");
            }
            RegisterLevelVo vo = new RegisterLevelVo();
            vo.setRegisterId(e.getId());
            vo.setRegisterDeptName(e.getRegisterDeptName());
            vo.setUserName(e.getUserName());
            if (ObjectUtil.isEmpty(e.getRegisterNoticeVo())) {
                throw new ServiceException("数据异常，没有录取通知");
            }
            vo.setJobNumber(e.getRegisterNoticeVo().getJobNumber());
            vo.setDurationName(e.getDurationStartDate() + " - " + e.getDurationEndDate() + "（" + e.getDurationName() + "）");
            vo.setUnitHospitalName(e.getUnitHospitalName());
            vo.setCultureStatusName(e.getCultureStatusName());
            vo.setScoreLevel(e.getScoreLevel());
            targetList.add(vo);
        });
        util.exportExcel(ServletUtils.getResponse(), targetList, "Sheet1");
    }

    @Override
    public AjaxResult levelImport(MultipartFile file) {
        boolean isSecretary = SecurityUtils.hasRole(RoleConstants.SECRETARY.getRoleKey());
        if (!isSecretary) {
            throw new ServiceException("只有教学秘书角色可以导入、导出考评等级！");
        }
        ExcelUtil<RegisterLevelVo> util = new ExcelUtil<>(RegisterLevelVo.class);
        List<RegisterLevelVo> importList;
        try {
            importList = util.importExcel(file.getInputStream());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (CollUtil.isEmpty(importList)) {
            throw new ServiceException("导入数据为空");
        }
        List<TRegisterInfo> targetList = new ArrayList<>();
        for (RegisterLevelVo vo : importList) {
            if (StrUtil.isEmpty(vo.getScoreLevel())) {
                throw new ServiceException("评分等级不能为空");
            }
            List<String> levelList = Arrays.asList("优", "良", "合格", "不合格");
            if (!CollUtil.contains(levelList, vo.getScoreLevel())) {
                throw new ServiceException("评分等级只有四个选项：" + CollUtil.join(levelList, "、"));
            }
            targetList.add(new TRegisterInfo()
                    .setId(vo.getRegisterId())
                    .setScoreLevel(vo.getScoreLevel()));
        }
        registerInfoService.updateBatchById(targetList);
        return AjaxResult.success("导入完成");
    }

    @Override
    public void exportManual(Long[] ids) {
        TRegisterInfoVo entity = new TRegisterInfoVo();
        entity.setNoAuth(true);
        entity.setRegisterIdList(Arrays.asList(ids));
        List<TRegisterInfoVo> list = registerInfoService.queryAll(entity);
        if (CollUtil.isEmpty(list)) {
            throw new ServiceException("没有需要导出的数据");
        }
        String projectPath = System.getProperty("user.dir") + "/uploadPath";
        String date = DateUtil.format(DateUtil.date(), "yyyy/MM/dd/");
        String wordPath = projectPath + "/upload/" + date;
        List<File> sourceFiles = new ArrayList<>();
        list.forEach(e -> {
            try {
                String templatePath = projectPath + "/upload/template/register-manual.docx";
                // 文件路径
                String jobNumber = "";
                if (ObjectUtil.isNotEmpty(e.getRegisterNoticeVo())) {
                    jobNumber = e.getRegisterNoticeVo().getJobNumber();
                }
                String wordName = jobNumber + "-" + e.getUserName() + ".docx";
                File wordPathFile = FileUtil.file(wordPath);
                if (!wordPathFile.exists()) {
                    wordPathFile.mkdirs();
                }
                Map<String, Object> map = new HashMap<>(16);
                map.put("entity", e);
                if (!Objects.isNull(e.getGender())) {
                    map.put("gender", e.getGender().intValue() == 1 ? "男" : "女");
                } else {
                    map.put("gender", "");
                }
                map.put("notice", ObjectUtil.isNotEmpty(e.getRegisterNoticeVo()) ? e.getRegisterNoticeVo() : new TRegisterNoticeVo());
                map.put("manual", e.getRegisterManualVo());
                List<TRegisterAllocationVo> allocationVoList = e.getRegisterAllocationVoList();
                map.put("s1", 0);
                map.put("s2", 0);
                map.put("s3", 0);
                map.put("s4", 0);
                map.put("s5", 0);
                map.put("s6", 0);
                if (!allocationVoList.isEmpty()) {
                    map.put("s1", allocationVoList.stream().mapToDouble(vo -> vo.getScoreProfessional().doubleValue()).average().getAsDouble());
                    map.put("s2", allocationVoList.stream().mapToDouble(vo -> vo.getScoreLabour().doubleValue()).average().getAsDouble());
                    map.put("s3", allocationVoList.stream().mapToDouble(vo -> vo.getScoreDuty().doubleValue()).average().getAsDouble());
                    map.put("s4", allocationVoList.stream().mapToDouble(vo -> vo.getScoreOperate().doubleValue()).average().getAsDouble());
                    map.put("s5", allocationVoList.stream().mapToDouble(vo -> vo.getScoreMedicalHistory().doubleValue()).average().getAsDouble());
                    map.put("s6", allocationVoList.stream()
                            .mapToDouble(vo -> vo.getScoreProfessional().doubleValue()
                                    + vo.getScoreLabour().doubleValue()
                                    + vo.getScoreDuty().doubleValue()
                                    + vo.getScoreOperate().doubleValue()
                                    + vo.getScoreMedicalHistory().doubleValue())
                            .sum());
                }
                map.put("introduceFlag1", " ");
                map.put("introduceFlag2", " ");
                map.put("planFlag1", " ");
                map.put("planFlag2", " ");
                map.put("environmentFlag1", " ");
                map.put("environmentFlag2", " ");
                map.put("teacherFlag1", " ");
                map.put("teacherFlag2", " ");
                map.put("businessFlag1", " ");
                map.put("businessFlag2", " ");
                map.put("timecardFlag1", " ");
                map.put("timecardFlag2", " ");
                map.put("examineFlag1", " ");
                map.put("examineFlag2", " ");
                if (ObjectUtil.isNotEmpty(e.getRegisterManualVo())) {
                    map.put("introduceFlag1", CompareUtil.compare(e.getRegisterManualVo().getIntroduceFlag(), 1L) == 0 ? "√" : " ");
                    map.put("introduceFlag2", CompareUtil.compare(e.getRegisterManualVo().getIntroduceFlag(), 2L) == 0 ? "√" : " ");
                    map.put("planFlag1", CompareUtil.compare(e.getRegisterManualVo().getPlanFlag(), 1L) == 0 ? "√" : " ");
                    map.put("planFlag2", CompareUtil.compare(e.getRegisterManualVo().getPlanFlag(), 2L) == 0 ? "√" : " ");
                    map.put("environmentFlag1", CompareUtil.compare(e.getRegisterManualVo().getEnvironmentFlag(), 1L) == 0 ? "√" : " ");
                    map.put("environmentFlag2", CompareUtil.compare(e.getRegisterManualVo().getEnvironmentFlag(), 2L) == 0 ? "√" : " ");
                    map.put("teacherFlag1", CompareUtil.compare(e.getRegisterManualVo().getTeacherFlag(), 1L) == 0 ? "√" : " ");
                    map.put("teacherFlag2", CompareUtil.compare(e.getRegisterManualVo().getTeacherFlag(), 2L) == 0 ? "√" : " ");
                    map.put("businessFlag1", CompareUtil.compare(e.getRegisterManualVo().getBusinessFlag(), 1L) == 0 ? "√" : " ");
                    map.put("businessFlag2", CompareUtil.compare(e.getRegisterManualVo().getBusinessFlag(), 2L) == 0 ? "√" : " ");
                    map.put("timecardFlag1", CompareUtil.compare(e.getRegisterManualVo().getTimecardFlag(), 1L) == 0 ? "√" : " ");
                    map.put("timecardFlag2", CompareUtil.compare(e.getRegisterManualVo().getTimecardFlag(), 2L) == 0 ? "√" : " ");
                    map.put("examineFlag1", CompareUtil.compare(e.getRegisterManualVo().getExamineFlag(), 1L) == 0 ? "√" : " ");
                    map.put("examineFlag2", CompareUtil.compare(e.getRegisterManualVo().getExamineFlag(), 2L) == 0 ? "√" : " ");
                }
                List<TRegisterLeaveVo> leaveVoList = e.getRegisterLeaveVoList();
                leaveVoList.forEach(leave -> {
                    if (CompareUtil.compare(leave.getLeaveType(), 1L) == 0) {
                        leave.setLeaveTypeName("事假");
                    } else if (CompareUtil.compare(leave.getLeaveType(), 2L) == 0) {
                        leave.setLeaveTypeName("病假");
                    }
                });
                map.put("leaveVoList", leaveVoList);
                map.put("leaveType1", leaveVoList.stream().filter(leave -> CompareUtil.compare(leave.getLeaveType(), 1L) == 0)
                        .map(TRegisterLeaveVo::getLeaveDuration)
                        .reduce(BigDecimal.ZERO, BigDecimal::add).intValue());
                map.put("leaveType2", leaveVoList.stream().filter(leave -> CompareUtil.compare(leave.getLeaveType(), 2L) == 0)
                        .map(TRegisterLeaveVo::getLeaveDuration)
                        .reduce(BigDecimal.ZERO, BigDecimal::add).intValue());
                TRegisterWorkVo registerWorkVo = new TRegisterWorkVo();
                registerWorkVo.setRegisterId(e.getId());
                List<TRegisterWorkVo> tRegisterWorkVos = registerWorkService.queryAll(registerWorkVo);
                List<String> workLastNewList = new ArrayList<>();
                if(tRegisterWorkVos!=null && !tRegisterWorkVos.isEmpty()){
                    List<TRegisterWorkVo> tRegisterWorkVosSort = tRegisterWorkVos.stream()
                            .sorted(Comparator.comparing(TRegisterWorkVo::getStartDate).reversed())
                            .collect(Collectors.toList());
                    for (TRegisterWorkVo workVo : tRegisterWorkVosSort) {
                        String workLastNew=workVo.getStartDate() +"-" +(StringUtils.isNotEmpty(workVo.getEndDate())?workVo.getEndDate():"至今") +" "+workVo.getHospitalName()+"," +workVo.getTechnicalTitle();
                        workLastNewList.add(workLastNew);
                    }
                }
                map.put("work", workLastNewList);
                map.put("workLastNew", StringUtils.join(workLastNewList,"\r\n"));
                ZipSecureFile.setMinInflateRatio(0);
                XWPFDocument document = WordExportUtil.exportWord07(templatePath, map);
                FileOutputStream os = new FileOutputStream(wordPath + wordName);
                document.write(os);
                os.flush();
                os.close();
                sourceFiles.add(FileUtil.file(wordPath + wordName));
            } catch (Exception ex) {
                log.error(ex.getMessage());
                throw new ServiceException("文件下载异常，请联系管理员");
            }
        });
        // 压缩
        String dateFormat = DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN);
        File targetZipFile = FileUtil.file(projectPath + "/upload/" + date + "导出学员手册-" + dateFormat + ".zip");
        ZipFileUtils.generateZip(targetZipFile, sourceFiles, false);
        for (File sourceFile : sourceFiles) {
            FileUtil.del(sourceFile);
        }
        ZipFileUtils.downloadZip(targetZipFile, ServletUtils.getResponse());
    }

    @Override
    public void exportManualPdf(Long id) {
        TRegisterInfoVo entity = new TRegisterInfoVo();
        entity.setNoAuth(true);
        entity.setRegisterIdList(Collections.singletonList(id));
        List<TRegisterInfoVo> list = registerInfoService.queryAll(entity);
        if (CollUtil.isEmpty(list)) {
            throw new ServiceException("没有需要导出的数据");
        }
        String projectPath = System.getProperty("user.dir") + "/uploadPath";
        String date = DateUtil.format(DateUtil.date(), "yyyy/MM/dd/");
        String filePath = projectPath + "/upload/" + date;
        TRegisterInfoVo tRegisterInfoVo = list.get(0);
        HttpServletResponse response = ServletUtils.getResponse();
        try {
            // 文件路径
            String jobNumber = "";
            if (ObjectUtil.isNotEmpty(tRegisterInfoVo.getRegisterNoticeVo())) {
                jobNumber = tRegisterInfoVo.getRegisterNoticeVo().getJobNumber();
            }
            File wordPathFile = FileUtil.file(filePath);
            if (!wordPathFile.exists()) {
                wordPathFile.mkdirs();
            }
            Map<String, Object> map = new HashMap<>(16);
            map.put("entity", tRegisterInfoVo);
            if (!Objects.isNull(tRegisterInfoVo.getGender())) {
                map.put("gender", tRegisterInfoVo.getGender().intValue() == 1 ? "男" : "女");
            } else {
                map.put("gender", "");
            }
            if(tRegisterInfoVo.getRegisterNoticeVo()!=null && StringUtils.isNotEmpty(tRegisterInfoVo.getLeaveStatusDesc())){
                tRegisterInfoVo.getRegisterNoticeVo().setEndDate( tRegisterInfoVo.getRegisterNoticeVo().getEndDate()+"("+tRegisterInfoVo.getLeaveStatusDesc()+")");
            }
            map.put("notice", ObjectUtil.isNotEmpty(tRegisterInfoVo.getRegisterNoticeVo()) ? tRegisterInfoVo.getRegisterNoticeVo() : new TRegisterNoticeVo());
            map.put("manual", tRegisterInfoVo.getRegisterManualVo());
            List<TRegisterAllocationVo> allocationVoList = tRegisterInfoVo.getRegisterAllocationVoList();
            map.put("s1", "");
            map.put("s2", "");
            map.put("s3", "");
            map.put("s4", "");
            map.put("s5", "");
            map.put("s6", "");
            if (!allocationVoList.isEmpty()) {
                map.put("s1", allocationVoList.stream().mapToDouble(vo -> vo.getScoreProfessional().doubleValue()).average().getAsDouble());
                map.put("s2", allocationVoList.stream().mapToDouble(vo -> vo.getScoreLabour().doubleValue()).average().getAsDouble());
                map.put("s3", allocationVoList.stream().mapToDouble(vo -> vo.getScoreDuty().doubleValue()).average().getAsDouble());
                map.put("s4", allocationVoList.stream().mapToDouble(vo -> vo.getScoreOperate().doubleValue()).average().getAsDouble());
                map.put("s5", allocationVoList.stream().mapToDouble(vo -> vo.getScoreMedicalHistory().doubleValue()).average().getAsDouble());
                map.put("s6", allocationVoList.stream()
                        .mapToDouble(vo -> vo.getScoreProfessional().doubleValue()
                                + vo.getScoreLabour().doubleValue()
                                + vo.getScoreDuty().doubleValue()
                                + vo.getScoreOperate().doubleValue()
                                + vo.getScoreMedicalHistory().doubleValue())
                        .sum()/allocationVoList.size());
            }
            map.put("introduceFlag1", " ");
            map.put("introduceFlag2", " ");
            map.put("planFlag1", " ");
            map.put("planFlag2", " ");
            map.put("environmentFlag1", " ");
            map.put("environmentFlag2", " ");
            map.put("teacherFlag1", " ");
            map.put("teacherFlag2", " ");
            map.put("businessFlag1", " ");
            map.put("businessFlag2", " ");
            map.put("timecardFlag1", " ");
            map.put("timecardFlag2", " ");
            map.put("examineFlag1", " ");
            map.put("examineFlag2", " ");
            if (ObjectUtil.isNotEmpty(tRegisterInfoVo.getRegisterManualVo())) {
                map.put("introduceFlag1", CompareUtil.compare(tRegisterInfoVo.getRegisterManualVo().getIntroduceFlag(), 1L) == 0 ? "√" : " ");
                map.put("introduceFlag2", CompareUtil.compare(tRegisterInfoVo.getRegisterManualVo().getIntroduceFlag(), 2L) == 0 ? "√" : " ");
                map.put("planFlag1", CompareUtil.compare(tRegisterInfoVo.getRegisterManualVo().getPlanFlag(), 1L) == 0 ? "√" : " ");
                map.put("planFlag2", CompareUtil.compare(tRegisterInfoVo.getRegisterManualVo().getPlanFlag(), 2L) == 0 ? "√" : " ");
                map.put("environmentFlag1", CompareUtil.compare(tRegisterInfoVo.getRegisterManualVo().getEnvironmentFlag(), 1L) == 0 ? "√" : " ");
                map.put("environmentFlag2", CompareUtil.compare(tRegisterInfoVo.getRegisterManualVo().getEnvironmentFlag(), 2L) == 0 ? "√" : " ");
                map.put("teacherFlag1", CompareUtil.compare(tRegisterInfoVo.getRegisterManualVo().getTeacherFlag(), 1L) == 0 ? "√" : " ");
                map.put("teacherFlag2", CompareUtil.compare(tRegisterInfoVo.getRegisterManualVo().getTeacherFlag(), 2L) == 0 ? "√" : " ");
                map.put("businessFlag1", CompareUtil.compare(tRegisterInfoVo.getRegisterManualVo().getBusinessFlag(), 1L) == 0 ? "√" : " ");
                map.put("businessFlag2", CompareUtil.compare(tRegisterInfoVo.getRegisterManualVo().getBusinessFlag(), 2L) == 0 ? "√" : " ");
                map.put("timecardFlag1", CompareUtil.compare(tRegisterInfoVo.getRegisterManualVo().getTimecardFlag(), 1L) == 0 ? "√" : " ");
                map.put("timecardFlag2", CompareUtil.compare(tRegisterInfoVo.getRegisterManualVo().getTimecardFlag(), 2L) == 0 ? "√" : " ");
                map.put("examineFlag1", CompareUtil.compare(tRegisterInfoVo.getRegisterManualVo().getExamineFlag(), 1L) == 0 ? "√" : " ");
                map.put("examineFlag2", CompareUtil.compare(tRegisterInfoVo.getRegisterManualVo().getExamineFlag(), 2L) == 0 ? "√" : " ");
            }
            List<TRegisterLeaveVo> leaveVoList = tRegisterInfoVo.getRegisterLeaveVoList();
            leaveVoList.forEach(leave -> {
                if (CompareUtil.compare(leave.getLeaveType(), 1L) == 0) {
                    leave.setLeaveTypeName("事假");
                } else if (CompareUtil.compare(leave.getLeaveType(), 2L) == 0) {
                    leave.setLeaveTypeName("病假");
                }
                leave.setLeaveDateStr(leave.getLeaveStartDate()+" - "+leave.getLeaveEndDate());
            });
            map.put("leaveType1", leaveVoList.stream().filter(leave -> CompareUtil.compare(leave.getLeaveType(), 1L) == 0)
                    .map(leave -> {
                        // 获取原始请假时长
                        BigDecimal leaveDuration = leave.getLeaveDuration();
                        // 获取销假时长（若无销假则默认为0）
                        BigDecimal closeDuration = Optional.ofNullable(leave.getCloseDuration())
                                .orElse(BigDecimal.ZERO);
                        // 计算实际时长（请假-销假，且不小于0）
                        return leaveDuration.subtract(closeDuration).max(BigDecimal.ZERO);
                    })
                    .reduce(BigDecimal.ZERO, BigDecimal::add).intValue());
            map.put("leaveType2", leaveVoList.stream().filter(leave -> CompareUtil.compare(leave.getLeaveType(), 2L) == 0)
                    .map(leave -> {
                        // 获取原始请假时长
                        BigDecimal leaveDuration = leave.getLeaveDuration();
                        // 获取销假时长（若无销假则默认为0）
                        BigDecimal closeDuration = Optional.ofNullable(leave.getCloseDuration())
                                .orElse(BigDecimal.ZERO);
                        // 计算实际时长（请假-销假，且不小于0）
                        return leaveDuration.subtract(closeDuration).max(BigDecimal.ZERO);
                    })
                    .reduce(BigDecimal.ZERO, BigDecimal::add).intValue());
            // 当数据不足16格时，填充剩余的空格
            int leaveSize = leaveVoList.size();
            if(leaveSize<15){
                for (int i = 0; i < 15 - leaveSize; i++) {
                    leaveVoList.add(new TRegisterLeaveVo());
                }
            }
            map.put("leaveVoList", leaveVoList);
            TRegisterWorkVo registerWorkVo = new TRegisterWorkVo();
            registerWorkVo.setRegisterId(id);
            List<TRegisterWorkVo> tRegisterWorkVos = registerWorkService.queryAll(registerWorkVo);
            List<String> workLastNewList = new ArrayList<>();
            if(tRegisterWorkVos!=null && !tRegisterWorkVos.isEmpty()){
                List<TRegisterWorkVo> tRegisterWorkVosSort = tRegisterWorkVos.stream()
                        .sorted(Comparator.comparing(TRegisterWorkVo::getStartDate).reversed())
                        .collect(Collectors.toList());
                for (TRegisterWorkVo workVo : tRegisterWorkVosSort) {
                    String workLastNew=workVo.getStartDate() +"-" +(StringUtils.isNotEmpty(workVo.getEndDate())?workVo.getEndDate():"至今") +" "+workVo.getHospitalName()+"," +workVo.getTechnicalTitle();
                    workLastNewList.add(workLastNew);
                }
            }
            map.put("work", workLastNewList);
            map.put("workLastNew", StringUtils.join(workLastNewList,"\r\n"));
            String pdfFileName = jobNumber + "-" + tRegisterInfoVo.getUserName() + ".pdf";
            String wordFileName = jobNumber + "-" + tRegisterInfoVo.getUserName() + ".docx";
            String templatePath = projectPath + "/upload/template/register-manual.docx";

            XWPFDocument document = WordExportUtil.exportWord07(templatePath, map);
            FileOutputStream os = new FileOutputStream(filePath + wordFileName);
            document.write(os);
            os.flush();
            os.close();
            // 创建一个新的Word文档
            com.aspose.words.Document doc = new com.aspose.words.Document(Files.newInputStream(Paths.get(filePath + wordFileName)));
            OutputStream pdfOutputStream = Files.newOutputStream(Paths.get(filePath + pdfFileName));
            doc.getBuiltInDocumentProperties().setTitle(pdfFileName);
            doc.save(pdfOutputStream, com.aspose.words.SaveFormat.PDF);
            // 设置响应头
            String showName = URLEncoder.encode(pdfFileName, "UTF-8");
            response.setHeader("Content-Type", "application/pdf");
            // 输出PDF字节流
            Files.copy(Paths.get(filePath + pdfFileName), response.getOutputStream());
            response.getOutputStream().flush();
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            throw new ServiceException("文件下载异常，请联系管理员");
        }
    }

    @Override
    public AjaxResult saveScoreRow(TRegisterAllocationVo entity) {
        entity.setScoreStatus(ScoreStatusEnums.SUCCESS.getCode());
        registerAllocationService.updateById(entity);
        updateScoreTotal(entity.getRegisterId());
        SysUser user = SecurityUtils.getLoginUser().getUser();
        List<String> roleList = new ArrayList<>();
        if(user.getRoles()!=null){
            for (SysRole role : user.getRoles()) {
                roleList.add(role.getRoleName());
            }
        }
        TUserOptLog tUserOptLog = new TUserOptLog()
                .setOptDesc("考评评分")
                .setOptTime(entity.getUpdateTime())
                .setFromUserid(user.getUserId().toString())
                .setFromUsername(user.getNickName())
                .setFromRolename(com.ruoyi.common.utils.StringUtils.join(roleList,","))
                .setToUserid(entity.getRegisterId().toString());
        tUserOptLogService.save(tUserOptLog);
        return AjaxResult.success();
    }

    /**
     * 更新评分
     *
     * @param registerId
     */
    private void updateScoreTotal(Long registerId) {
        List<TRegisterAllocation> registerAllocationList = registerAllocationService.list(Wrappers.<TRegisterAllocation>lambdaQuery()
                .eq(TRegisterAllocation::getRegisterId, registerId)
                .eq(TRegisterAllocation::getScoreStatus, ScoreStatusEnums.SUCCESS.getCode()));
        BigDecimal sum = registerAllocationList.stream().map(TRegisterAllocation::getScoreTotal)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal scoreTotal = BigDecimal.ZERO;
        if (CompareUtil.compare(sum, BigDecimal.ZERO) != 0) {
            scoreTotal = NumberUtil.div(sum, registerAllocationList.size() * 100).multiply(BigDecimal.valueOf(100));
        }
        registerInfoService.updateById(new TRegisterInfo()
                .setId(registerId)
                .setScoreTotal(scoreTotal));
    }

    @Override
    public AjaxResult saveLeave(TRegisterLeaveVo entity) {
        // 判断请假时间是否交叉
        Integer count = registerLeaveService.lambdaQuery()
                .eq(TRegisterLeave::getRegisterId, entity.getRegisterId())
                .le(TRegisterLeave::getLeaveStartDate, entity.getLeaveStartDate())
                .ge(TRegisterLeave::getLeaveEndDate, entity.getLeaveStartDate())
                .or(wq -> wq.eq(TRegisterLeave::getRegisterId, entity.getRegisterId())
                        .le(TRegisterLeave::getLeaveStartDate, entity.getLeaveEndDate())
                        .ge(TRegisterLeave::getLeaveEndDate, entity.getLeaveEndDate()))
                .count();
        if (count > 0) {
            return AjaxResult.error("请假时间不能重叠，请修改时间");
        }
        if (CompareUtil.compare(entity.getLeaveStatus(), LeaveStatusEnums.FULL.getCode()) == 0) {
            registerLeaveService.update(Wrappers.<TRegisterLeave>lambdaUpdate()
                    .set(TRegisterLeave::getEffectiveStatus, LeaveEffectiveStatusEnums.DELETE.getCode())
                    .eq(TRegisterLeave::getRegisterId, entity.getRegisterId()));
        }
        entity.setEffectiveStatus(LeaveEffectiveStatusEnums.NORMAL.getCode());
        registerLeaveService.save(entity);
        registerInfoService.updateById(new TRegisterInfo().setId(entity.getRegisterId()).setLeaveStatus(entity.getLeaveStatus()));
        // 超过14天延期毕业
        List<TRegisterLeave> registerLeaveList = registerLeaveService.lambdaQuery().eq(TRegisterLeave::getRegisterId, entity.getRegisterId()).list();
        BigDecimal duration = registerLeaveList.stream().map(TRegisterLeave::getLeaveDuration).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (CompareUtil.compare(duration, new BigDecimal("14")) >= 0) {
            TRegisterNoticeVo registerNoticeVo = registerNoticeService.queryByRegisterId(entity.getRegisterId());
            String endDate = DateUtil.offsetDay(DateUtil.parse(registerNoticeVo.getEndDateDefault(),"yyyy/MM/dd"), duration.setScale(0, RoundingMode.HALF_UP).intValue()).toString("yyyy/MM/dd");
            registerNoticeVo.setEndDate(endDate);
            registerNoticeService.updateById(registerNoticeVo);
        }
        return AjaxResult.success();
    }

    @Override
    public AjaxResult saveLoseLeave(TRegisterLeaveVo entity) {
        entity.setEffectiveStatus(LeaveEffectiveStatusEnums.CLOSE.getCode());
        registerLeaveService.updateById(entity);
        // 销假后，改为在院
        registerInfoService.updateById(new TRegisterInfo().setId(entity.getRegisterId()).setCurrentStatus(CurrentStatusEnums.NO.getCode()));
        return AjaxResult.success();
    }

    @Override
    public AjaxResult saveGraduation(TAuditLogVo entity) {
        // 三级审核：秘书、主任(科室负责人)、管理员
        SysUser user = SecurityUtils.getLoginUser().getUser();
        Long userId = user.getUserId();
        boolean isSecretary = SecurityUtils.hasRole(RoleConstants.SECRETARY.getRoleKey());
        boolean isDeptManage = SecurityUtils.hasRole(RoleConstants.DEPT_MANAGE.getRoleKey());
        boolean isSystem = SecurityUtils.hasRole(RoleConstants.SYSTEM.getRoleKey());
        boolean isAdmin = SecurityUtils.hasRole(RoleConstants.ADMIN.getRoleKey());
        if (isSystem || isAdmin) {

            for (Long registerId : entity.getRegisterIdList()) {
                TRegisterInfo registerInfo = registerInfoService.getById(registerId);
                TAuditLog auditLog = new TAuditLog()
                        .setType(2L)
                        .setRegisterId(registerId)
                        .setAuditUserId(userId)
                        .setAuditStatus(entity.getAuditStatus())
                        .setRemark(entity.getRemark());
            /*if (CompareUtil.compare(registerInfo.getGraduationAuditLevel(), 0L) == 0 && !isSecretary) {
                throw new ServiceException("学员[" + registerInfo.getUserName() + "]需要使用秘书进行一级审批");
            }
            if (CompareUtil.compare(registerInfo.getGraduationAuditLevel(), 1L) == 0 && !isDeptManage) {
                throw new ServiceException("学员[" + registerInfo.getUserName() + "]需要使用主任(科室负责人)进行二级审批");
            }
            if (CompareUtil.compare(registerInfo.getGraduationAuditLevel(), 2L) == 0 && !isSystem) {
                throw new ServiceException("学员[" + registerInfo.getUserName() + "]需要使用管理员进行三级审批");
            }*/
                registerInfo.setGraduationAuditLevel(registerInfo.getGraduationAuditLevel() + 1);
                auditLog.setAuditLevel(registerInfo.getGraduationAuditLevel());
            /*// 转审中，且通过，进行下一级
            if (CompareUtil.compare(registerInfo.getGraduationAuditLevel(), 3L) < 0) {
                if (CompareUtil.compare(entity.getAuditStatus(), 1L) == 0) {
                    registerInfo.setGraduationAuditStatus(GraduationAuditStatusEnums.WAIT.getCode());
                } else {
                    registerInfo.setGraduationAuditStatus(GraduationAuditStatusEnums.FAIL.getCode());
                }
            } else {
                if (CompareUtil.compare(entity.getAuditStatus(), 1L) == 0) {
                    registerInfo.setCultureStatus(CultureStatusEnums.END.getCode());
                    registerInfo.setGraduationAuditStatus(GraduationAuditStatusEnums.SUCCESS.getCode());
                } else {
                    registerInfo.setGraduationAuditStatus(GraduationAuditStatusEnums.FAIL.getCode());
                }
            }*/
                if (CompareUtil.compare(entity.getAuditStatus(), 1L) == 0) {
                    if(Objects.equals(registerInfo.getGraduationStatus(), GraduationStatusEnums.BEFORE.getCode())){
                        // 当结业为提前结业时，将通知表的离院日期为结业审批时的时间
                        Date auditDate = registerInfo.getUpdateTime();
                        String endTimeStr = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_X, auditDate);
                        TRegisterNoticeVo noticeVo = new TRegisterNoticeVo();
                        noticeVo.setRegisterId(registerId);
                        List<TRegisterNoticeVo> tRegisterNoticeVos = registerNoticeService.queryAll(noticeVo);
                        if(tRegisterNoticeVos!=null && !tRegisterNoticeVos.isEmpty()){
                            TRegisterNotice registerNotice = tRegisterNoticeVos.get(0);
                            if (ObjectUtil.isNotEmpty(registerNotice) && registerInfo.getGraduationStatus().equals(GraduationStatusEnums.BEFORE.getCode())) {
                                registerNotice.setEndDate(endTimeStr);
                                registerNoticeService.updateById(registerNotice);
                            }
                        }
                    }
                    registerInfo.setGraduationAuditStatus(GraduationAuditStatusEnums.SUCCESS.getCode());
                    registerInfo.setCultureStatus(CultureStatusEnums.END.getCode());
                } else {
                    registerInfo.setGraduationAuditStatus(GraduationAuditStatusEnums.FAIL.getCode());
                }
                auditLogService.save(auditLog);
                registerInfoService.updateById(registerInfo);

                List<String> roleList = new ArrayList<>();
                if(user.getRoles()!=null){
                    for (SysRole role : user.getRoles()) {
                        roleList.add(role.getRoleName());
                    }
                }
                String optDesc = "";
                if (CompareUtil.compare(entity.getAuditStatus(), 1L) == 0) {
                    optDesc = "审批意见【同意】";
                } else if (CompareUtil.compare(entity.getAuditStatus(), 2L) == 0) {
                    optDesc = "审批意见【退回】";
                } else if (CompareUtil.compare(entity.getAuditStatus(), 3L) == 0) {
                    optDesc = "审批意见【拒绝】";
                }
                TUserOptLog tUserOptLog = new TUserOptLog()
                        .setOptDesc("结业审批："+optDesc)
                        .setOptTime(auditLog.getUpdateTime())
                        .setFromUserid(user.getUserId().toString())
                        .setFromUsername(user.getNickName())
                        .setFromRolename(StringUtils.join(roleList,","))
                        .setToUserid(registerId.toString())
                        .setToUsername(registerInfo.getUserName());
                tUserOptLogService.save(tUserOptLog);

            }
        }else {
            throw new ServiceException("只有管理员角色可以审核！");
        }
        return AjaxResult.success();
    }

    @Override
    public AjaxResult saveEvaluateNotice(TMessageInfoVo entity) {
        for (Long registerId : entity.getRegisterIdList()) {
            TRegisterInfo registerInfo = registerInfoService.getById(registerId);
            // 系统消息
            messageInfoService.save(new TMessageInfo()
                    .setType(MessageTypeEnums.MESSAGE.getCode())
                    .setRefId(entity.getId())
                    .setSendUserId(SecurityUtils.getUserId())
                    .setReceiveUserId(registerInfo.getUserId())
                    .setContent(entity.getContent())
                    .setReadStatus(MessageReadStatusEnums.UNREAD.getCode()));
        }
        return AjaxResult.success();
    }


    @Override
    public void queryByCertificate(TRegisterInfoVo entity) {
        // 查看短期证书
        if (ObjectUtil.isEmpty(entity) || ObjectUtil.isEmpty(entity.getId())) {
            throw new ServiceException("进修 ID 不能为空");
        }
        TRegisterInfoVo registerInfo = registerInfoService.queryById(entity.getId());
        TRegisterNotice registerNotice = registerNoticeService.lambdaQuery()
                .eq(TRegisterNotice::getRegisterId, registerInfo.getId())
                .one();
        if (ObjectUtil.isEmpty(registerInfo)) {
            throw new ServiceException("报名信息未找到");
        }
        if (ObjectUtil.isEmpty(registerNotice)) {
            throw new ServiceException("通知书不能为空");
        }
        String projectPath = System.getProperty("user.dir") + "/uploadPath";
        String templatePath = projectPath + "/upload/template/register-certificate.jpg";
        try {
            Image src = Toolkit.getDefaultToolkit().getImage(templatePath);
            BufferedImage image = BufferedImageBuilder.toBufferedImage(src);
            Graphics2D g = image.createGraphics();
            Font font = new Font("宋体", Font.BOLD, 80);
            g.setFont(font);
            g.setColor(Color.black);
            g.drawString(registerInfo.getUserName(), 500, 950);
            g.drawString(registerInfo.getIdCard(), 1750, 950);
            g.drawString(registerNotice.getStartDate().substring(0, 4), 550, 1150);
            g.drawString(registerNotice.getStartDate().substring(5, 7), 950, 1150);
            g.drawString(registerNotice.getStartDate().substring(8, 10), 1250, 1150);
            g.drawString(registerNotice.getEndDate().substring(0, 4), 1650, 1150);
            g.drawString(registerNotice.getEndDate().substring(5, 7), 2000, 1150);
            g.drawString(registerNotice.getEndDate().substring(8, 10), 2350, 1150);
            g.drawString(registerInfo.getRegisterDeptName(), 1000, 1350);
            long day = DateUtil.betweenDay(DateUtil.parse(registerNotice.getStartDate()), DateUtil.parse(registerNotice.getEndDate()), true);
            g.drawString(String.valueOf(day), 2800, 1350);
            String currentDate = DateUtil.today();
            g.drawString(currentDate.substring(0, 4), 2320, 2100);
            g.drawString(currentDate.substring(5, 7), 2660, 2100);
            g.drawString(currentDate.substring(8, 10), 2900, 2100);
            g.dispose();
            ServletOutputStream outputStream = ServletUtils.getResponse().getOutputStream();
            ImageIO.write(image, "jpg", outputStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 使用 ImageIO.read(file); 压缩后的图片会变红改为使用
     * Toolkit.getDefaultToolkit().getImage
     */
    public static class BufferedImageBuilder {
        public static BufferedImage toBufferedImage(Image image) {
            if (image instanceof BufferedImage) {
                return (BufferedImage) image;
            }
            // This code ensures that all the pixels in the image are loaded
            image = new ImageIcon(image).getImage();
            BufferedImage bimage = null;
            GraphicsEnvironment ge = GraphicsEnvironment
                    .getLocalGraphicsEnvironment();
            try {
                int transparency = Transparency.OPAQUE;
                GraphicsDevice gs = ge.getDefaultScreenDevice();
                GraphicsConfiguration gc = gs.getDefaultConfiguration();
                bimage = gc.createCompatibleImage(image.getWidth(null),
                        image.getHeight(null), transparency);
            } catch (HeadlessException e) {
                // The system does not have a screen
            }
            if (bimage == null) {
                // Create a buffered image using the default color model
                int type = BufferedImage.TYPE_INT_RGB;
                bimage = new BufferedImage(image.getWidth(null),
                        image.getHeight(null), type);
            }
            // Copy image to buffered image
            Graphics g = bimage.createGraphics();
            // Paint the image onto the buffered image
            g.drawImage(image, 0, 0, null);
            g.dispose();
            return bimage;
        }
    }

    @Override
    public void importRegisterTemplate() {
        List<RegisterImportVo> list = new ArrayList<>();
        RegisterImportVo vo = new RegisterImportVo();
        vo.setName("张三（示例数据）");
        vo.setPhone("18386474459");
        vo.setGender(1L);
        vo.setBirthday("1998-09-20");
        vo.setIdCard("522323199809209182");
        vo.setHighestDegree("本科");
        vo.setTechnicalTitle("高级医师");
        vo.setRegisterDeptName("ICU");
        vo.setDurationDate("2024-09-2024-12");
        vo.setUnitName("上海市静安区中心医院");
        vo.setUnitProvince("上海市");
        vo.setUnitCity("上海市");
        vo.setUnitDistrict("静安区");
        vo.setUnitAddress("西康路259号");
        vo.setWorkTelephone("021-61578000");
        vo.setZipCode("200040");
        vo.setEducationAskObjective("略");
        vo.setElectronicIdPhoto("");
        list.add(vo);
        EasyPoiExcelUtils.exportExcel(list, RegisterImportVo.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult importRegister(MultipartFile file) {
        ImportParams params = new ImportParams();
        List<RegisterImportVo> list = null;
        try {
            list = ExcelImportUtil.importExcel(file.getInputStream(), RegisterImportVo.class, params);
        } catch (Exception e) {
            throw new ServiceException("文件读取异常");
        }
        if (CollUtil.isEmpty(list)) {
            return AjaxResult.error("导入数据为空");
        }
        if (list.size() == 1 && "张三（示例数据）".equals(list.get(0).getName())) {
            return AjaxResult.error("导入数据为空");
        }
        list = list.stream().filter(e -> !"张三（示例数据）".equals(e.getName())).collect(Collectors.toList());
        List<TRegisterInfoVo> entityList = new ArrayList<>();
        List<TCooperateInfo> cooperateInfoList = cooperateInfoService.list();
        List<SysArea> allArea = sysAreaService.getAllArea();
        Map<String,String> areaCache = new HashMap<>();
        for (SysArea sysArea : allArea) {
            areaCache.put(sysArea.getExtName(),sysArea.getId());
        }
        allArea.clear();
        for (RegisterImportVo vo : list) {
            if (StrUtil.isEmpty(vo.getName())) {
                throw new ServiceException("姓名不能为空，请检查数据格式");
            }
            if (StrUtil.isEmpty(vo.getPhone())) {
                throw new ServiceException("联系电话不能为空，请检查数据格式");
            }
            if (StrUtil.isEmpty(vo.getIdCard())) {
                throw new ServiceException("身份证号不能为空，请检查数据格式");
            }
            if (StrUtil.isEmpty(vo.getRegisterDeptName())) {
                throw new ServiceException("进修科室不能为空，请检查数据格式");
            }
            if (StrUtil.isEmpty(vo.getDurationDate())) {
                throw new ServiceException("进修时间不能为空，请检查数据格式");
            }
            if (!BusinessUtils.validateDurationDateFormat(vo.getDurationDate())) {
                throw new ServiceException("进修时间格式错误，请检查数据格式");
            }
            if (StrUtil.isEmpty(vo.getUnitName())) {
                throw new ServiceException("单位名称不能为空，请检查数据格式");
            }
            if (StrUtil.isEmpty(vo.getElectronicIdPhoto())) {
                throw new ServiceException("电子证件照不能为空");
            }
            TRegisterDept registerDept = registerDeptService.lambdaQuery().eq(TRegisterDept::getRegisterDeptName, vo.getRegisterDeptName()).one();
            if (ObjectUtil.isEmpty(registerDept)) {
                throw new ServiceException("进修科室【" + vo.getRegisterDeptName() + "】不存在，请检查数据格式");
            }
            String imageName = new File(vo.getElectronicIdPhoto()).getName();
            String pathName = DateUtil.format(DateUtil.date(), "/yyyy/MM/dd/") + IdUtil.fastUUID() + ".png";
            String sourcePath = RuoYiConfig.getProfile() + "/temp/" + imageName;
            String targetPath = RuoYiConfig.getUploadPath() + pathName;
            FileUtil.move(new File(sourcePath), new File(targetPath), true);
            vo.setElectronicIdPhoto("/profile/upload" + pathName);

            SysUser sysUser = sysUserService.lambdaQuery().eq(SysUser::getUserName, vo.getPhone()).one();
            if (ObjectUtil.isEmpty(sysUser)) {
                sysUser = new SysUser();
                sysUser.setUserName(vo.getPhone());
                sysUser.setNickName(vo.getName());
                sysUser.setPhonenumber(vo.getPhone());
                sysUser.setIdCard(vo.getIdCard());
                sysUser.setPassword(SecurityUtils.encryptPassword("123456"));
                sysUser.setRoleIds(new Long[]{RoleConstants.STUDENT.getRoleId()});
                sysUserService.insertUser(sysUser);
            }

            TRegisterInfoVo entity = new TRegisterInfoVo();
            BeanUtil.copyProperties(vo, entity);
            entity.setUserId(sysUser.getUserId());
            entity.setRegisterDeptId(registerDept.getId());
            entity.setAgentUserId(SecurityUtils.getLoginUser().getUserId());
            String[] durationDate = vo.getDurationDate().split("-");
            entity.setDurationStartDate(durationDate[0]);
            entity.setDurationEndDate(durationDate[1]);
            entity.setApplicantName(vo.getName());
            entity.setUserName(vo.getName());
            entity.setUnitHospitalName(vo.getUnitName());
            entity.setPhoneNumber(vo.getPhone());

            // 合作单位
            for (TCooperateInfo cooperateInfo : cooperateInfoList) {
                if (StrUtil.equals(entity.getUnitHospitalName(), cooperateInfo.getProjectName())) {
                    String[] split = cooperateInfo.getCooperateTime().split("-");
                    Date startTime = DateUtil.parse(split[0]);
                    Date endTime = DateUtil.parse(split[1]);
                    if (DateUtil.isIn(DateUtil.date(), startTime, endTime)) {
                        String[] deptArray = cooperateInfo.getCooperateDept().split("、");
                        if (StrUtil.equals(cooperateInfo.getCooperateDept(), "全部") || ArrayUtil.contains(deptArray, registerDept.getRegisterDeptName())) {
                            entity.setSourceUnitType("合作单位");
                            break;
                        }
                    }
                }
            }
            entity.setIdCardType("居民身份证");
            entity.setAuditStatus(AuditStatusEnums.SUCCESS.getCode());
            entity.setApplyTime(DateUtil.date());
            entity.setAuditLevel(0L);
            entity.setGraduationAuditLevel(0L);
            entity.setNoticeStatus(NoticeStatusEnums.NO.getCode());
            entity.setSureStatus(1L);
            entity.setAllocationStatus(0L);
            entity.setCultureStatus(0L);
            entity.setCurrentStatus(0L);
            entity.setLeaveStatus(0L);
            entity.setGraduationStatus(0L);
            entity.setGraduationAuditStatus(0L);
            entity.setEvaluateStatus(1L);
            entity.setPayStatus(0L);
            entity.setLabelSubmit(1L);
            entity.setLabelEngage(1L);
            // 此处判断，当省市区数据都不为空且全量地址为空的情况下，查询数据给全量地址增加数据
            if(StringUtils.isNotEmpty(entity.getUnitProvince())
                    && StringUtils.isNotEmpty(entity.getUnitCity())
                    && StringUtils.isNotEmpty(entity.getUnitDistrict())){
                // 通过省市区获取到地市编码id
                entity.setUnitCodes(areaCache.get(entity.getUnitProvince())+","+areaCache.get(entity.getUnitCity())+","+areaCache.get(entity.getUnitDistrict()));
            }
            entityList.add(entity);
        }
        List<TRegisterInfo> registerInfoList = BeanUtil.copyToList(entityList, TRegisterInfo.class);
        registerInfoService.saveBatch(registerInfoList);
        areaCache.clear();
        return AjaxResult.success("导入成功，共计：" + registerInfoList.size() + "条记录");
    }

    @Override
    @Transactional
    public AjaxResult batchSaveLeave(TRegisterLeaveAddVo entity) {
        for (TRegisterLeaveVo leaveItem : entity.getLeaveItems()) {
            AjaxResult ajaxResult = saveLeave(leaveItem);
            if(MapUtils.getIntValue(ajaxResult,CODE_TAG)!=200){
                throw new BaseException("与上次请假时间冲突，请重新选择");
            }
        }
        return AjaxResult.success();
    }

    @Override
    @Transactional
    public AjaxResult batchSaveLoseLeave(TRegisterLeaveAddVo entity) {
        for (TRegisterLeaveVo leaveItem : entity.getLeaveItems()) {
            AjaxResult ajaxResult = saveLoseLeave(leaveItem);
            if(MapUtils.getIntValue(ajaxResult,CODE_TAG)!=200){
                throw new BaseException("批量销假异常");
            }
        }
        return AjaxResult.success();
    }

}
